package com.pbd.server;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.hibernate.proxy.HibernateProxy;

public class DataObjectPopulation {

	public static List populateDataObjects(List dataList, String[] columnExpressions) {
		dataList = populateDataObject(dataList);
		populateDataList(dataList, columnExpressions);
		return dataList;
	}

	private static List populateDataObject(List dataList) {
		int dataListSize = dataList == null ? 0 : dataList.size();
		if (dataListSize == 0)
			return null;
		List dataObjectList = new ArrayList();
		for (int startIndex = 0; startIndex < dataListSize; startIndex++) {
			Object obj = dataList.get(startIndex);
			if (obj == null)
				continue;
			if (!dataObjectList.contains(obj))
				dataObjectList.add(obj);
		}
		return dataObjectList;
	}

	private static void populateDataList(List dataObjectList, String[] columnExpressions) {
		int dataObjectListSize = dataObjectList == null ? 0 : dataObjectList.size();
		for (int i = 0; i < dataObjectListSize; i++) {
			Object dataObject = dataObjectList.get(i);
			int columnExpsLength = columnExpressions == null ? 0 : columnExpressions.length;
			for (int j = 0; j < columnExpsLength; j++) {
				String colExpression = columnExpressions[j];
				if (colExpression.indexOf(".") == -1) {
					List columnExpressionList = getEvaluatedColumnExpression(columnExpressions, colExpression);
					int columnExpressionListSize = columnExpressionList == null ? 0 : columnExpressionList.size();
					for (int k = 0; k < columnExpressionListSize; k++) {
						String columnExpression = (String) columnExpressionList.get(k);
						populateDataObject(columnExpression, dataObject);
					}
				}
			}
		}
	}

	private static void populateDataObject(String columnExpression, Object tempDataObject) {
		if (columnExpression != null && columnExpression.trim().length() > 0) {
			Object dataObject = null;
			boolean execute = false;
			while (columnExpression.indexOf(".") != -1) {
				String colExpStr = columnExpression;
				String colExpression = columnExpression.substring(0, columnExpression.indexOf("."));
				columnExpression = columnExpression.substring(columnExpression.indexOf(".") + 1);
				if (dataObject != null) {
					if (implementsCollection(dataObject.getClass())) {
						List dataObjects = (List) dataObject;
						int dataObjectsSize = dataObjects == null ? 0 : dataObjects.size();
						for (int i = 0; i < dataObjectsSize; i++) {
							dataObject = dataObjects.get(i);
							populateDataObject(colExpStr, dataObject);
						}
						execute = true;
						break;
					} else{
						dataObject = populateObject(dataObject, colExpression);
					}
				} else if (tempDataObject != null) {
					if (implementsCollection(tempDataObject.getClass())) {
						List dataObjects = (List) tempDataObject;
						int dataObjectsSize = dataObjects == null ? 0 : dataObjects.size();
						for (int i = 0; i < dataObjectsSize; i++) {
							dataObject = dataObjects.get(i);
							populateDataObject(colExpStr, dataObject);
						}
						execute = true;
						break;
					} else
						dataObject = populateObject(tempDataObject, colExpression);
				}
			}
			if (!execute) {
				if (dataObject != null || tempDataObject != null) {
					if (dataObject != null)
						if (implementsCollection(dataObject.getClass())) {
							List dataObjects = (List) dataObject;
							int dataObjectsSize = dataObjects == null ? 0 : dataObjects.size();
							for (int i = 0; i < dataObjectsSize; i++) {
								dataObject = dataObjects.get(i);
								populateDataObject(columnExpression, dataObject);
							}
						} else{
							dataObject = populateObject(dataObject, columnExpression);
							System.out.println("dataObject --->"+dataObject);
						}
					else if (tempDataObject != null) {
						if (implementsCollection(tempDataObject.getClass())) {
							List dataObjects = (List) dataObject;
							int dataObjectsSize = dataObjects == null ? 0 : dataObjects.size();
							for (int i = 0; i < dataObjectsSize; i++) {
								dataObject = dataObjects.get(i);
								populateDataObject(columnExpression, dataObject);
							}
						} else{
							dataObject = populateObject(tempDataObject, columnExpression);
							System.out.println("dataObject --->"+dataObject);
						}
					}
				}
			}
		}
	}

	private static Object populateObject(Object dataObject, String columnExpression) {
		if (dataObject != null) {
			Class clazz = dataObject.getClass();
			Method[] methods = clazz.getDeclaredMethods();
			int methodsLength = methods == null ? 0 : methods.length;
			for (int i = 0; i < methodsLength; i++) {
				Method method = methods[i];
				String methodName = method.getName();
				if (!methodName.startsWith("get"))
					continue;
				methodName = methodName.substring(3);
				if (methodName.equalsIgnoreCase(columnExpression)) {
					Class returnTypeClazz = method.getReturnType();
					methodName = "set" + methodName;
					try {
						if (implementsCollection(returnTypeClazz)) {
							Collection sourceColl = (Collection) method.invoke(dataObject, null);
							Class[] paramType = new Class[1];
							paramType[0] = returnTypeClazz;
							Method setterMethod = clazz.getMethod(methodName, paramType);
							Object param[] = new Object[1];
							param[0] = sourceColl;
							setterMethod.invoke(dataObject, param);
							return sourceColl;
						} else if (!isPrimitive(returnTypeClazz) && !(returnTypeClazz == HibernateProxy.class) && !(returnTypeClazz == Class.class) && !(returnTypeClazz == Iterator.class)) {
							Object dataObjectValue = method.invoke(dataObject, null);
							Class[] paramType = new Class[1];
							paramType[0] = returnTypeClazz;
							Method setterMethod = clazz.getMethod(methodName, paramType);
							Object param[] = new Object[1];
							param[0] = dataObjectValue;
							setterMethod.invoke(dataObject, param);
							return dataObjectValue;
						}
					} catch (SecurityException e) {
						throw new RuntimeException("Exception while fetching the mutator method for column [" + columnExpression + "] Found exception is [" + e);
					} catch (IllegalArgumentException e) {
						throw new RuntimeException("Exception while fetching the mutator method for column [" + columnExpression + "] Found exception is [" + e);
					} catch (IllegalAccessException e) {
						throw new RuntimeException("Exception while setting the data for column [" + columnExpression + "] Found exception is [" + e);
					} catch (InvocationTargetException e) {
						throw new RuntimeException("Exception while setting the data for column [" + columnExpression + "] Found exception is [" + e);
					} catch (NoSuchMethodException e) {
						throw new RuntimeException("Exception while fetching the mutator method for column [" + columnExpression + "] Found exception is [" + e);
					}
				}
			}
		}
		return null;
	}

	private static List getEvaluatedColumnExpression(String[] columnExpressions, String colExpression) {
		int columnExpressionsLength = columnExpressions == null ? 0 : columnExpressions.length;
		List columnList = new ArrayList();
		for (int i = 0; i < columnExpressionsLength; i++) {
			String columnExpression = columnExpressions[i];
			if (columnExpression.startsWith(colExpression))
				columnList.add(columnExpression);
		}
		int maxCount = getColumnExpressionsList(columnList);
		if (maxCount == 0)
			return columnList;
		return reOrganizeList(columnList, maxCount);
	}

	private static List reOrganizeList(List columnList, int maxCount) {
		List reOrganizeList = new ArrayList();
		int columnListSize = columnList == null ? 0 : columnList.size();
		for (int i = 0; i <= maxCount; i++) {
			for (int j = 0; j < columnListSize; j++) {
				String columnExpression = (String) columnList.get(j);
				StringTokenizer tokentizer = new StringTokenizer(columnExpression, ".");
				int countTokens = tokentizer.countTokens();
				if (i == countTokens)
					reOrganizeList.add(columnExpression);
			}
		}
		return reOrganizeList;
	}

	private static int getColumnExpressionsList(List columnList) {
		int columnListSize = columnList == null ? 0 : columnList.size();
		int maxTokens = 0;
		for (int i = 0; i < columnListSize; i++) {
			String columnExpression = (String) columnList.get(i);
			StringTokenizer tokentizer = new StringTokenizer(columnExpression, ".");
			int countTokens = tokentizer.countTokens();
			if (maxTokens < countTokens)
				maxTokens = countTokens;
		}
		return maxTokens;
	}

	private static boolean implementsCollection(Class objectClass) {
		if (objectClass == Collection.class)
			return true;
		ArrayList interfaces = new ArrayList();
		Class superClass = objectClass;
		do {
			interfaces.addAll(Arrays.asList(superClass.getInterfaces()));
			superClass = superClass.getSuperclass();
		} while (superClass != null);
		boolean isCollection = false;
		for (int j = 0; j < interfaces.size(); j++) {
			if ((interfaces.get(j) == Collection.class) || (interfaces.get(j) == List.class)) {
				isCollection = true;
				break;
			}
		}
		return isCollection;
	}

	private static boolean isPrimitive(Class objectClass) {
		if (objectClass.isPrimitive() || objectClass == Boolean.class || objectClass == Character.class || objectClass == Byte.class || objectClass == Short.class || objectClass == Integer.class || objectClass == Long.class || objectClass == Float.class || objectClass == Double.class || objectClass == String.class || objectClass == Void.class)
			return true;
		else
			return false;
	}

}
